S3 웹호스팅 사이트에 API Gateway + Lambda + DynamoDB 구성으로 서버리스 구축해 보기
안녕하세요 클래스메소드 김재욱(Kim Jaewook) 입니다. 이번에는 S3 웹호스팅 사이트에 API Gateway + Lambda + DynamoDB 구성으로 서버리스를 구축해 보는 과정을 정리해 봤습니다.
하고 싶은 것
S3 버킷에서 정적인 Web Hosting 사이트를 운영하고 있지만, API Gateway + Lambda + DynamoDB를 추가해 동적인 Web Hosting 사이트로 구성할 생각입니다.
여기서 프론트는 S3 버킷에서 HTML과 JS로 관리하며 백에서는 API Gateway, Lambda, DynamoDB를 통해 관리합니다.
결과적으로 본 블로그에서는 Web Hosting 되고 있는 S3 버킷에 접근해 DynamoDB 테이블의 항목을 불러오거나 저장하는 작업을 진행합니다.
DynamoDB 생성
먼저 DynamoDB 콘솔 화면으로 들어와「테이블 생성」버튼을 클릭합니다.
테이블 이름과 파티션 키를 입력합니다.
파티션 키의 경우 테이블의 기본 키가 되는 것으로, 주로 항목을 검색하는 데 사용됩니다.
그 외 DynamoDB 스펙은 가장 낮은 스펙으로 설정하고, DynamoDB 테이블 생성을 끝마칩니다.
※ 테스트 환경이기 때문에 상기와 같은 설정을 진행하지만, 본인 상황에 맞게 설정을 진행합니다.
이어서 생성한 테이블에서 항목을 생성합니다.
「작업」→「항목 생성」을 클릭합니다.
id가 테이블의 기본 키 값이 되며, 추가적으로 name, description을 입력합니다.
반환된 항목을 살펴보면 조금 전 입력한 항목이 추가된 것을 확인할 수 있습니다.
DynamoDB에 액세스할 권한 생성
Lambda에서 DynamoDB로 액세스하기 위해서는 권한이 필요합니다.
IAM 콘솔 화면에서「역할」로 들어와서 권한을 생성합니다.
- 「AWS」서비스를 선택합니다.
- 「Lambda」를 선택합니다.
- AmazonDynamoDBFullAccess
- AWSLambdaDynamoDBExecutionRole
상기 권한을 설정하여 IAM Role을 생성합니다.
Lambda 함수 생성
IAM Role을 생성했다면, 이어서 Lambda를 생성합니다.
- 런타임은 Python을 선택합니다.
- 본 블로그에서는 Python 코드를 통해 DynamoDB에 액세스할 생각이기 때문에 Python을 선택했습니다.
- 시용 권한의 경우 조금 전 생성한 IAM Role을 선택합니다.
import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table("test-db") def lambda_handler(event, context): response = table.scan() return response['Items']
이어서 Lambda에 상기 코드를 입력합니다.
test-db에 접근하여 테이블의 항목을 response에 저장하고 리턴합니다.
즉 테이블의 항목을 불러오는 get의 역할을 하는 함수입니다.
import boto3 import json dynamodb = boto3.resource('dynamodb') table = dynamodb.Table("test-db") def lambda_handler(event, context): inserted_data = { 'id': event['id'], 'name': event['name'], 'description': event['description'], # 필요한 경우 추가 필드 추가 } table.put_item(Item=inserted_data) return { 'statusCode': 200, 'body': json.dumps(inserted_data) }
이어서 동일한 방법으로 Lambda 함수를 하나 더 생성하여 상기 코드를 입력합니다.
상기 코드의 경우 HTML 페이지에서 값을 입력받고, DynamoDB 테이블에 항목을 저장합니다.
get Lambda 함수의 경우 이미 DynamoDB 테이블에 항목이 들어가 있기 때문에 제대로 불러와지는지 확인을 위해「Test」버튼을 클릭합니다.
Lambda 함수가 문제 없이 동작한다면 다음 API Gateway를 생성합니다.
API Gateway 생성
API Gateway 콘솔 화면에서「API 생성」버튼을 클릭합니다.
REST API를 선택합니다.
- 「새 API」를 선택합니다.
- API 이름을 입력합니다.
- API 엔드포인트 유형은「지역」을 선택합니다.
- 「API 생성」버튼을 클릭합니다.
「메서드 생성」을 클릭합니다.
- 메서드 유형의 경우 GET을 선택합니다.
- 통합 유형은 Lambda를 선택합니다.
- 마지막으로 Lambda 함수의 경우 get 작업을 수행하는 Lambda 함수를 선택합니다.
똑같은 방법으로 POST 유형으로 메서드를 생성합니다.
이어서 CORS를 활성화합니다.
생성한 메서드를 선택하고 CORS를 활성화합니다.
여기서 CORS를 활성화하는 이유는 Web Hosting을 하고 있는 test.html 혹은 index.html 뿐 만 아니라 다른 오리진으로의 액세스가 있기 때문입니다.
- 동일한 오리진
- http://testjaewook.com/web1/index.html
- http://testjaewook.com/web2/index.html
- http://testjaewook.com/web1:80
- 다른 오리진
- https://testjaewook.com/web1
- http://www.testjaewook.com/web1
- http://aws.testjaewook.com/web1
- http://testjaewook.com/web1:44301
- http://testjaewook.com/web1:8080
이번에는 Ajax 등을 사용하여, API Gateway 즉 다른 오리진에 액세스하기 때문에 CORS를 활성화할 필요가 있습니다.
Web Hosting을 위한 S3 버킷 설정
Web Hosting을 위해서 퍼블릭 액세스가 가능한 상태로 S3 버킷을 생성합니다.
※ 현재는 퍼블릭 액세스가 가능한 상태지만, CloudFront를 이용한다면, 퍼블릭 액세스를 차단할 수 있습니다.
S3 버킷의 속성에서「정적 웹 사이트 호스팅」을 찾아 편집을 클릭합니다.
- 정적 웹 사이트 호스팅을 활성화합니다.
- 호스팅 유형의 경우「정적 웹 사이트 호스팅」을 선택합니다.
- 본인의 파일명에 맞게 인덱스 문서를 입력합니다.
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>test</title> </head> <body> <!-- GetData --> <h1>List</h1> <div id="responseData"></div> <!-- InputData --> <div> <h1>Input</h1> <table> <tr> <td><input type="text" id="id" placeholder="ID"></td> <td><input type="text" id="name" placeholder="Name"></td> <td><input type="text" id="description" placeholder="Description"></td> </tr> </table> <button onclick="sendDataToLambda()">Send Data</button> </div> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script src="testjs.js"></script> </body> </html>
이제 마지막으로 메인 페이지가 될 HTML 코드를 입력합니다.
간단하게 DynamoDB 테이블의 항목을 출력하고, 테이블에 항목을 삽입할 텍스트와 버튼을 생성했습니다.
// send data from s3 -> lambda(dynamoDB) async function sendDataToLambda() { try { const htmlid = document.getElementById('id').value; const htmlname = document.getElementById('name').value; const htmldescription = document.getElementById('description').value; const dataToSend = { id: htmlid, name: htmlname, description: htmldescription }; const response = await axios.post('api gateway URL', dataToSend); console.log('Response from Lambda:', response.data); } catch (error) { console.error('Error sending data:', error); } } // get data from lambda(dynamoDB) async function getDataToLambda() { try { const response = await axios.get('api gateway URL'); const getData = response.data; const listContainer = document.getElementById('responseData'); getData.forEach(item => { const listItem = document.createElement('p'); listItem.textContent = `ID: ${item.id}, Name: ${item.name}, Description: ${item.description}`; listContainer.appendChild(listItem); }); } catch (error) { console.error('Error reading data:', error); } } getDataToLambda();
이어서 js 코드입니다.
HTML 한 페이지에 코드를 전부 써내려가도 상관은 없지만, 가독성을 위해 HTML과 js 코드를 나누어 만들었습니다.
마지막으로 S3 버킷에 파일을 업로드합니다.
결과 확인
Web Hosting 사이트의 URL로 접속해 보면, DynamoDB 테이블을 생성할 때 만들어두었던 항목이 출력되는 것을 볼 수 있습니다.
이어서 텍스트를 입력하고 버튼을 클릭해 보면, 상기 이미지와 같이 항목이 DynamoDB 테이블에 입력되며, 결과로써 List에 출력이 되는 것을 확인할 수 있습니다.
이렇게 프론트와 백을 완전히 나누어 웹 사이트를 구축할 수도 있으며, 서버 구축 없는 서버리스 환경을 만들 수도 있습니다.
본 블로그 게시글을 읽고 궁금한 사항이 있으신 분들은 [email protected]로 보내주시면 감사하겠습니다.